﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Data.DBModels;
using Data.MyDbContext;
using Utilities;
using System.Security.Cryptography;

namespace DAL.Repositories
{
    public class UserRepository : RepositoryPublic<User>
    {
         public UserRepository()
        {
            DB = new WebStudioDbContext();
        }

        public UserRepository(DbContext db)
            : base(db)
        { }

        public UserRepository(DbContextFactory db)
            : base(db)
        { }

        public override bool Update(User user)
        {
            if (user == null) throw new ArgumentNullException("Object to be Updated cannot be null...");
            user.Password = GetSHA256(user.Password);
            DB.Entry(user).State = EntityState.Modified;

            return (DB.SaveChanges() > 0) ? true : false;
        }


        public override bool Add(User user)
        {
            if (user == null) throw new ArgumentNullException("Object to be added to DB cannot be null...");
            user.Password = GetSHA256(user.Password);
            
            DB.Set<User>().Add(user);

            return (DB.SaveChanges() > 0) ? true : false;
        }

        /// <summary>
        /// Returns the UserId of the user
        /// </summary>
        /// <param name="UserName">UserName of the user</param>
        /// <returns>UserId Guid on success, Empty Guid on failure</returns>
        public Guid GetUserId(string userName)
        {
            if (String.IsNullOrEmpty(userName)) throw new NullOrEmptyStringException();

            try
            {
                return DB.Set<User>().First(user => user.Username == userName).Id;
            }
            //source is null or empty(for where, select and First)
            catch (ArgumentNullException) { }
            //we dont get any result from db
            catch (InvalidOperationException) { }

            return Guid.Empty;
        }

        /// <summary>
        /// Returns the UserId of the user
        /// </summary>
        /// <param name="UserName">UserName of the user</param>
        /// <exception cref="NullOrEmptyStringException">userName cannot be null or empty...</exception>
        /// <returns>UserId Guid on success, Empty Guid on failure</returns>
        public User GetUser(string userName)
        {
            if (String.IsNullOrEmpty(userName)) throw new NullOrEmptyStringException("userName cannot be null or empty...");

            return First(user => user.Username == userName);
        }

        public bool ChangePassword(Guid userId, string newPassword)
        {
            if (!userId.IsValid()) throw new NullOrEmptyStringException("guid userId can not be null or empty");
            if (String.IsNullOrEmpty(newPassword.Trim())) throw new NullOrEmptyStringException("new Password can not be null or empty");

            User user = Find(userId);
            if (user == null)
                return false;

            user.Password = newPassword;

            return Update(user);
        }

        public bool CreateUser(User userData)
        {
            if (userData == null) throw new ArgumentNullException("User to be created cannot be null...");

            userData.Password = userData.Password.ToString();
            return Add(userData);
        }

        public bool VerifyUser(string username, string password)
        {
            if (String.IsNullOrEmpty(username)) throw new NullOrEmptyStringException("username of account can not be null or empty");
            if (String.IsNullOrEmpty(password)) throw new NullOrEmptyStringException("password of account can not be null or empty");

            string pass = GetSHA256(password);
            var user = First(item => item.Username == username && item.Password == pass );

            if (user == null)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Takes in a block of text, applies a SHA256 Hash to the text.
        /// </summary>
        /// <param name="text">The text you with to have a SHA256 Hash applied too.</param>
        /// <returns>Returns a SHA256 Hashed value of the text passed in.</returns>
        private string GetSHA256(string text)
        {
            UnicodeEncoding UE = new UnicodeEncoding();
            byte[] message = UE.GetBytes(text);

            string hashedPassword = String.Empty;

            byte[] hashValue = (new SHA256Managed()).ComputeHash(message);
            foreach (byte x in hashValue)
            {
                hashedPassword += String.Format("{0:x2}", x);
            }
            return hashedPassword;
        }
    }
}
